#include "StdAfx.h"

CMpqHandler* Singleton<CMpqHandler>::m_instance = 0;

static const char* archives[] = { "lichking.MPQ", "expansion.MPQ", "common-2.MPQ", "common.MPQ" };

CMpqHandler::CMpqHandler()
{
}

bool CMpqHandler::Init()
{
	std::string wowPath = WoWFolder::GetWoWPath();
	if(wowPath == "")
	{
		StartupException s;
		s.szTitle = "Error";
		s.szBody = "Unable to load the path to your wow folder from Config\\Main.txt or the registry!";
		throw s;
	}

	wowPath += "\\data\\";

	m_szLocale = WoWFolder::GetLocale();
	if(m_szLocale == "")
	{
		StartupException s;
		s.szTitle = "Error";
		s.szBody = "Unable to detect the locale of your wow installation! Please make sure that a valid wow is installed!";
		throw s;
	}

	if(!WoWFolder::GetWoWVersion(m_build) || m_build < 10958 || m_build > 12340)
	{
		StartupException s;
		s.szBody = "You do not have patch 3.3 installed. This beta only works for clients with patch 3.3 and higher.\n"\
			"As soon as the first tests give a positive feedback also older patches (down to 3.0) will be supported.";
		s.szTitle = "Invalid patch";
		throw s;
	}

	HANDLE hTexts;
	if(SFileOpenArchive("textures.MPQ", 0, 0, &hTexts))
	{
		MPQEntry e = 
		{
			"textures.MPQ",
			hTexts
		};

		mArchives.push_back(e);
	}
	else
	{
		StartupException s;
		s.szTitle = "Missing file";
		s.szBody = "Could not find textures.MPQ in the folder. Run the launcher to repair your installation!";
		throw s;
	}

	if(SFileOpenArchive("models.MPQ", 0, 0, &hTexts))
	{
		//m_archives["models.MPQ"] = hTexts;
		MPQEntry e = 
		{
			"models.MPQ",
			hTexts
		};

		mArchives.push_back(e);
	}
	else
	{
		StartupException s;
		s.szTitle = "Missing file";
		s.szBody = "Could not find models.MPQ in the folder. Run the launcher to repair your installation!";
		throw s;
	}

	if(SFileOpenArchive("wmo.MPQ", 0, 0, &hTexts))
	{
		MPQEntry e = 
		{
			"wmo.MPQ",
			hTexts
		};

		mArchives.push_back(e);
	}
	else
	{
		StartupException s;
		s.szTitle = "Missing file";
		s.szBody = "Could not find wmo.MPQ in the folder. Run the launcher to repair your installation!";
		throw s;
	}

	HANDLE hArchive;
	char patch1[MAX_PATH];
	sprintf_s<MAX_PATH>(patch1, "%s%s\\locale-%s.MPQ", wowPath.c_str(), m_szLocale.c_str(), m_szLocale.c_str());
	Log::Print("%s(%u): Loading MPQ '%s' ...", __FILE__, __LINE__, patch1);
	if(SFileOpenArchive(patch1, 0, 0, &hArchive))
	{
		Log::Print("... opened!\n");
		MPQEntry e = 
		{
			patch1,
			hArchive
		};

		mArchives.push_back(e);
	}

	for(ui32 i = 0; i < 10; ++i)
	{
		HANDLE hArchive;
		char path[255];
		sprintf(path, "%spatch-%u.MPQ", wowPath.c_str(), i);
		char patch[255];
		sprintf(patch, "patch-%u.MPQ", i);
		if(SFileOpenArchive(path, 0, 0, &hArchive))
		{
			Log::Print("%s(%u): Loading MPQ '%s'\n", __FILE__, __LINE__, path);
			m_patches[patch] = hArchive;
			MPQEntry e = 
			{
				patch,
				hArchive
			};

			mArchives.push_back(e);
		}

		// Somewhere weve to load patch.MPQ but there could also be a file named patch-0.MPQ
		if(i == 0)
		{
			sprintf(path, "%s%s\\patch-%s.MPQ", wowPath.c_str(), m_szLocale.c_str(), m_szLocale.c_str());
			sprintf(patch, "patch-%s.MPQ", m_szLocale.c_str(), m_szLocale.c_str());
			if(SFileOpenArchive(path, 0, 0, &hArchive))
			{
				Log::Print("%s(%u): Loading MPQ '%s'\n", __FILE__, __LINE__, path);
				m_patches[patch] = hArchive;
			}

			sprintf(path, "%spatch.MPQ", wowPath.c_str());
			sprintf(patch, "patch.MPQ");
			if(SFileOpenArchive(path, 0, 0, &hArchive))
			{
				Log::Print("%s(%u): Loading MPQ '%s'\n", __FILE__, __LINE__, path);
				m_patches[patch] = hArchive;
				MPQEntry e = 
				{
					patch,
					hArchive
				};

				mArchives.push_back(e);
			}
		}

		sprintf(path, "%s%s\\patch-%s-%u.MPQ", wowPath.c_str(), m_szLocale.c_str(), m_szLocale.c_str(), i);
		sprintf(patch, "patch-%s-%u.MPQ", m_szLocale.c_str(), i);
		if(SFileOpenArchive(path, 0, 0, &hArchive))
		{
			Log::Print("%s(%u): Loading MPQ '%s'\n", __FILE__, __LINE__, path);
			m_patches[patch] = hArchive;
			MPQEntry e = 
			{
				patch,
				hArchive
			};

			mArchives.push_back(e);
		}
	}

	std::vector<std::string> m_missings;

	for(ui32 i = 0; i < 4; ++i)
	{
		HANDLE hArchive;
		char path[1024];
		sprintf_s<1024>(path, "%s%s", wowPath.c_str(), archives[i]);
		Log::Print("%s(%u): Loading MPQ '%s' ...", __FILE__, __LINE__, path);
		if(SFileOpenArchive(path, 0, 0, &hArchive))
		{
			Log::Print("... opened!\n");
			m_archives[archives[i]] = hArchive;
			MPQEntry e =
			{
				archives[i],
				hArchive
			};

			mArchives.push_back(e);
		}
		else
		{
			m_missings.push_back(archives[i]);
			Log::Print("... not found!\n");
		}
	}

	if(m_missings.size())
	{
		StartupException s;
		s.szTitle = "Missing file";
		s.szBody = "The following important files could not be found:\n";
		for(ui32 i = 0; i < m_missings.size(); ++i)
		{
			s.szBody += m_missings[i] + "\n";
		}
		s.szBody += "\nRepair WoW or check the path in the registry!";
		throw s;
	}

	SortArchives();

	return true;
}

void CMpqHandler::SortArchives()
{
	mArchives.sort(
	[] (MPQEntry& e1, MPQEntry& e2) -> int
	{
		using std::string;
		string strA = e1.archiveName;
		string strB = e2.archiveName;
		
		string::size_type patchIndexA = strA.find("patch");
		string::size_type patchIndexB = strB.find("patch");

		string::size_type npos = string::npos;
		if(patchIndexA != npos && patchIndexB == npos)
			return true;
		if(patchIndexB != npos && patchIndexA == npos)
			return false;

		if(patchIndexA == -1 && patchIndexB == -1)
			return (-strA.compare(strB)) < 0;

		string::size_type extIndexA = strA.rfind('.');
		string::size_type extIndexB = strB.rfind('.');

		char patchIdentA = strA.substr(extIndexA - 1, 1)[0];
		char patchIdentB = strB.substr(extIndexB - 1, 1)[0];

		char separatorA = strA.substr(extIndexA - 2, 1)[0];
		char separatorB = strB.substr(extIndexB - 2, 1)[0];

		if(separatorA != '-' && separatorB == '-')
			return false;
		if(separatorA == '-' && separatorB != '-')
			return true;

		if(patchIdentA == patchIdentB)
			return false;
		if(patchIdentA < patchIdentB)
			return false;
		if(patchIdentA > patchIdentB)
			return true;

		return false;
	}
	);
}

HANDLE CMpqHandler::GetFile(std::string szFile, FILE** pFile, std::string szArchive)
{
	if(pFile)
	{
		char path[MAX_PATH];
		sprintf(path, "Save\\%s", szFile.c_str());
		*pFile = fopen(path, "rb");
		if(*pFile)
			return MPQ_FILE_HANDLE;
	}

	for(auto itr = mArchives.begin(); itr != mArchives.end(); ++itr)
	{
		MPQEntry& e = (*itr);
		if(szArchive != "")
		{
			if(e.archiveName.compare(szArchive) != 0)
				continue;
		}

		HANDLE hFile;
		BOOL found = SFileOpenFileEx(e.archiveHandle, szFile.c_str(), 0, &hFile);
		if(!found)
			continue;
		return hFile;
	}

	return INVALID_HANDLE_VALUE;

	//if(szArchive != "")
	//{
	//	HANDLE hArchive = m_archives[szArchive];
	//	if(!hArchive)
	//		return INVALID_HANDLE_VALUE;
	//	HANDLE hFile;
	//	BOOL found = SFileOpenFileEx(hArchive, szFile.c_str(), 0, &hFile);
	//	if(!found)
	//		return INVALID_HANDLE_VALUE;
	//	return hFile;
	//}
	//else
	//{
	//	if(pFile)
	//	{
	//		char path[MAX_PATH];
	//		sprintf(path, "Save\\%s", szFile.c_str());
	//		*pFile = fopen(path, "rb");
	//		if(*pFile)
	//			return MPQ_FILE_HANDLE;
	//	}
	//	std::map<std::string, HANDLE>::iterator itr = m_patches.begin();
	//	std::map<std::string, HANDLE>::iterator end = m_patches.end();

	//	for( ; itr != end; ++itr)
	//	{
	//		HANDLE hFile;
	//		BOOL found = SFileOpenFileEx(itr->second, szFile.c_str(), 0, &hFile);
	//		if(found)
	//			return hFile;
	//	}

	//	itr = m_archives.begin();
	//	end = m_archives.end();

	//	for( ; itr != end; ++itr)
	//	{
	//		HANDLE hFile;
	//		BOOL found = SFileOpenFileEx(itr->second, szFile.c_str(), 0, &hFile);
	//		if(found)
	//			return hFile;
	//	}

	//	return INVALID_HANDLE_VALUE;
	//}
}